﻿// (c)2010 David Schwarz (david.schwarz@live.com)
// This file is licensed under GNU GPL v2 license, see more at http://www.gnu.org/licenses/gpl-2.0.html
namespace Z2.Editor
{

	#region Using

	using System;
	using System.Collections.Generic;
	using System.ComponentModel;
	using System.Diagnostics;
	using System.Drawing;
	using System.IO;
	using System.Text;
	using System.Threading;
	using System.Windows.Forms;
	using System.Xml;
	using vKapse.DDTool.Core;
	using vKapse.DDTool.Visual;
	using Z2.Editor.Properties;
	using System.Drawing.Drawing2D;
	using FormsTimer = System.Windows.Forms.Timer;

	#endregion

	/// <summary>
	/// Editor
	/// </summary>
	public partial class EditorForm : Form
	{

		#region Const

		private const string MAP_EXT = "Želvíci 2 Mapa|*.zmap";
		private const string OBJECTS_EXT = "*.xml";
		private const string DATA_PATH = @"Objects\";
		private const int TILE_SIZE = 32;

		#endregion

		#region Members

		#region Menu

		private FileTriggerGroup m_fileGroup;
		private MapTriggerGroup m_mapGroup;
		private HelpTriggerGroup m_helpGroup;
		private SettingsGroup m_settingsGroup;
		private List<LastMap> m_lastMaps;

		#endregion

		private int m_size = 1;
		private World m_map;
		private bool m_change;
		private Point m_camera;

		private bool m_mouseDown;

		private EditorFieldObject m_selectedObject;
		private Rectangle m_selectedObjectRectangle;

		private EditorObject m_selected;
		private EditorMode m_mode = EditorMode.Put;
		private Bitmap m_player;
		private Bitmap m_blank;
		private Point m_mouse;
		private Keys m_key;
		private bool m_keyPressed;

		private Point m_grab;
		private bool m_grabbing;

		private static Dictionary<string, EditorObject> _defs;
		private static Dictionary<string, ToolStripMenuItem> _groups;
		private List<string> m_backgrounds = new List<string>();

		private FormsTimer m_autosave;
		private TimeSpan m_autosaveTime = TimeSpan.FromMinutes(1);
		private DateTime m_lastAutoSave = DateTime.Now;

		#endregion

		#region Constructor

		/// <summary>
		/// Statický konstruktor
		/// </summary>
		static EditorForm()
		{
			_defs  = new Dictionary<string, EditorObject>();
			_groups = new Dictionary<string, ToolStripMenuItem>();
		}

		/// <summary>
		/// Konstruktor editoru
		/// </summary>
		public EditorForm()
		{
			this.InitializeComponent();
			this.Initialize();
		}

		#endregion

		#region Properties

		/// <summary>
		/// Vrací definice všech objekt a polí
		/// </summary>
		public static Dictionary<string, EditorObject> Definitions
		{
			get
			{
				return _defs;
			}
		}

		#endregion

		/// <summary>
		/// Metoda vrací nové pole pro mapu.
		/// </summary>
		/// <returns>
		/// Dle definice <see cref="EditorField"/> nebo <see cref="EditorFieldObject"/>.
		/// </returns>
		protected EditorField GetNewField()
		{
			if (this.m_selected == null)
			{
				throw new InvalidOperationException();
			}

			switch (this.m_selected.Type)
			{
				case FieldType.Enviroment:
					return new EditorField(this.m_selected);
				case FieldType.Object:
					return new EditorFieldObject(this.m_selected);
			}

			throw new NotImplementedException();
		}

		#region Methods

		/// <summary>
		/// Metoda spustí inicializaci editoru
		/// </summary>
		private void Initialize()
		{
			Debug.WriteLine("Initializing menu..");
			this.InitializeMenu();
			this.SetTitle();
			this.m_camera = new Point();
			Debug.WriteLine("Enumerating objects..");
			this.EnumerateObjects();
			this.m_blank = new Bitmap(EditorForm.TILE_SIZE, EditorForm.TILE_SIZE);
			Graphics.FromImage(this.m_blank).Clear(Color.Black);

			this.pnEditor.Paint += new PaintEventHandler		(this.Editor_Paint);
			this.pnEditor.SizeChanged += new EventHandler		(this.Editor_SizeChanged);
			this.pnEditor.MouseDown += new MouseEventHandler	(this.Editor_MouseDown);
			this.pnEditor.MouseMove += new MouseEventHandler	(this.Editor_MouseMove);
			this.pnEditor.MouseUp += new MouseEventHandler		(this.Editor_MouseUp);
			
			//this.DoubleBuffered = true;

			this.hScrollBar.Maximum = 1000;
			this.vScrollBar.Maximum = 1000;
			this.KeyPreview = true;

			this.m_player = (Bitmap)Bitmap.FromFile(string.Format("{0}player.png", EditorForm.DATA_PATH));

			MapTriggerGroup.Select.IsChecked = true;
			this.m_mode = EditorMode.Select;

			this.m_autosave = new FormsTimer { Interval = 1000 };
			this.m_autosave.Tick += new EventHandler(Autosave_Tick);
			this.m_autosave.Enabled = true;
			this.m_autosave.Start();

			Debug.WriteLine("Editor started.");
		}

		/// <summary>
		/// Metoda zinicializuje menu a toolbar
		/// </summary>
		private void InitializeMenu()
		{
			this.m_fileGroup = new FileTriggerGroup();
			this.m_mapGroup = new MapTriggerGroup();
			this.m_helpGroup = new HelpTriggerGroup();
			this.m_settingsGroup = new SettingsGroup();

			FileTriggerGroup.NewMap.Triggered += new EventHandler		(this.NewMap_Trigger);
			FileTriggerGroup.LoadMap.Triggered += new EventHandler		(this.LoadMap_Trigger);
			FileTriggerGroup.SaveMap.Triggered += new EventHandler		(this.SaveMap_Trigger);
			FileTriggerGroup.SaveMapAs.Triggered += new EventHandler	(this.SaveAsMap_Trigger);
			FileTriggerGroup.Exit.Triggered += new EventHandler			(this.Exit_Trigger);
			
			MapTriggerGroup.MapProperties.Triggered += new EventHandler	(this.MapProperties_Trigger);
			MapTriggerGroup.MapTest.Triggered += new EventHandler		(this.TestMap_Trigger);
			MapTriggerGroup.Script.Triggered += new EventHandler		(this.Script_Triggered);
			MapTriggerGroup.Delete.Triggered += new EventHandler		(this.miDelete_Click);
			MapTriggerGroup.Select.Triggered += new EventHandler		(this.miSelect_Click);
			MapTriggerGroup.Background.Triggered += new EventHandler	(this.Background_Triggered);

			SettingsGroup.AutoSave.Triggered += new EventHandler		(this.AutoSave_Triggered);

			HelpTriggerGroup.About.Triggered += new EventHandler		(this.About_Trigger);
			HelpTriggerGroup.Log.Triggered += new EventHandler			(this.Log_Triggered);

			this.AddTriggerGroup(this.m_fileGroup, AddTriggerMode.Both);
			this.AddTriggerGroup(this.m_mapGroup, AddTriggerMode.Both);
			this.AddTriggerGroup(this.m_settingsGroup, AddTriggerMode.Menu);
			this.AddTriggerGroup(this.m_helpGroup, AddTriggerMode.Menu);

			this.m_lastMaps = LastMap.GetLastMaps();

			this.cmEditorContext.Items.Add(new ToolStripSeparator());
			this.cmEditorContext.Items.AddRange(this.m_mapGroup.CreateMenu());

			MapTriggerGroup.Size.ComboBoxTextChanged += this.tbSize_TextChanged;
			SettingsGroup.AutoSave.IsChecked = true;
			
			this.RefreshLastMaps();
		}

		private void AddLastMap()
		{
			if (this.m_map != null)
			{
				if (this.m_lastMaps.Find(delegate(LastMap match)
				{
					return match.FileName == this.m_map.FileName;
				}) == null)
				{
					if (this.m_lastMaps.Count >= 10)
					{
						this.m_lastMaps.RemoveAt(0);
					}

					this.m_lastMaps.Add(new LastMap { FileName = this.m_map.FileName });
					LastMap.Save(this.m_lastMaps);
					this.RefreshLastMaps();
				}
			}
		}

		private void RefreshLastMaps()
		{
			foreach (object control in FileTriggerGroup.LastMaps.Associations)
			{
				if (control is ToolStripMenuItem)
				{
					ToolStripMenuItem item = (ToolStripMenuItem)control;
					if (this.m_lastMaps.Count == 0)
					{
						item.Visible = false;
						continue;
					}
					item.DropDownItems.Clear();
					item.Visible = true;
					foreach (LastMap lastMap in this.m_lastMaps)
					{
						if (!File.Exists(lastMap.FileName))
							continue;
						FileInfo fileInfo = new FileInfo(lastMap.FileName);
						string text = fileInfo.FullName;
						if (text.Length > 32)
							text = string.Format("..{0}", text.Substring(text.Length - 32, 32));
						ToolStripMenuItem lastMapItem = new ToolStripMenuItem(text);
						lastMapItem.Tag = fileInfo;
						lastMapItem.Click += new EventHandler(LastMapItem_Click);
						item.DropDownItems.Add(lastMapItem);
					}
				}
			}

		}

		/// <summary>
		/// Metoda načte veškeré definice objektů a políček uložené v adresáři s daty pro hru a editor
		/// </summary>
		private void EnumerateObjects()
		{
			string path = Path.Combine(Application.StartupPath, EditorForm.DATA_PATH);
			path = Path.Combine(path, "Definitions.xml");

			if (!File.Exists(path))
			{
				MessageBox.Show("Nebyly nalezeny žádné definice objektů a polí.", "Chyba", MessageBoxButtons.OK, MessageBoxIcon.Error);
				Application.Exit();
				return;
			}
			XmlDocument document = new XmlDocument();
			document.Load(path);

			XmlNodeList objects = document.GetElementsByTagName("object");
			XmlNodeList fields = document.GetElementsByTagName("field");
			XmlNodeList backgrounds = document.GetElementsByTagName("background");
			XmlNodeList items = document.GetElementsByTagName("item");
			
			foreach (XmlNode node in objects)
				this.LoadXmlData(node);
			foreach (XmlNode node in fields)
				this.LoadXmlData(node);
			foreach (XmlNode node in backgrounds)
				this.LoadBackground(node);
			foreach (XmlNode node in items)
				this.LoadXmlData(node);

		}

		/// <summary>
		/// Metoda přidá do menu a toolbaru skupinu triggerů
		/// </summary>
		/// <param name="group">Skupina sdružující triggery</param>
		/// <param name="onlyMenu"><strong>True</strong>, pokud se skupina nemá vytvářet do ToolBaru (tj. pouze menu)</param>
		private void AddTriggerGroup(TriggerGroup group, AddTriggerMode mode)
		{
			if (this.tsToolBarContainer == null)
			{
				return;
			}

			ToolStrip toolStrip = group.CreateToolStrip();
			ToolStripItem[] items = group.CreateMenu();

			if (items != null && (mode == AddTriggerMode.Menu || mode == AddTriggerMode.Both))
			{
				ToolStripMenuItem menuItem = new ToolStripMenuItem();
				menuItem.Text = group.Text;
				menuItem.DropDownItems.AddRange(items);
				this.msMainMenu.Top = 0;
				this.msMainMenu.Items.Add(menuItem);
			}

			if (toolStrip != null && toolStrip.Items.Count > 0 && (mode == AddTriggerMode.ToolBar || mode == AddTriggerMode.Both))
			{
				Control prev = this.tsToolBarContainer.TopToolStripPanel.Controls.Count > 0 ?
					this.tsToolBarContainer.TopToolStripPanel.Controls[this.tsToolBarContainer.TopToolStripPanel.Controls.Count - 1] : null;
				if (prev != null && prev.GetType() == typeof(MenuStrip)) prev = null;
				toolStrip.Left = prev == null ? 0 : prev.Left + prev.Width + 1;
				toolStrip.Top = this.msMainMenu.Top + this.msMainMenu.Height;
				this.tsToolBarContainer.TopToolStripPanel.Controls.Add(toolStrip);
			}

		}

		/// <summary>
		/// Metoda načte XML data definice objektu/políčka
		/// </summary>
		/// <param name="fileName">Název souboru s XML daty</param>
		private void LoadXmlData(XmlNode node)
		{
			string name = Convert.ToString(node.Attributes["Name"].Value).ToLower();
			EditorObject obj = new EditorObject(name, node);
			_defs.Add(name, obj);

			ToolStripMenuItem item = new ToolStripMenuItem(name, obj.GetBitmap());
			ToolStripMenuItem addToItem = null;
			ToolStripMenuItem group = null;
			bool isNewGroup = false;

			item.Tag = obj;
			item.Click += new EventHandler(this.Item_Click);

			if (!string.IsNullOrEmpty(obj.Group))
			{
				if (!_groups.ContainsKey(obj.Group))
				{
					_groups.Add(obj.Group, new ToolStripMenuItem(obj.Group));
					isNewGroup = true;
				}
				group = _groups[obj.Group];
			}

			switch (obj.Type)
			{
				case FieldType.Enviroment:
					addToItem = this.miEnviroment;
					break;
				case FieldType.Object:
					addToItem = this.miObject;
					break;
				default:
					throw new Exception(string.Format("Neznámý typ objektu: {0}.", (int)obj.Type));
			}

			if (isNewGroup && group != null)
			{
				addToItem.DropDownItems.Add(group);
			}
			if (group != null)
			{
				group.DropDownItems.Add(item);
			}
			else
			{
				addToItem.DropDownItems.Add(item);
			}
		}

		private void LoadBackground(XmlNode node)
		{
			string file =
				string.Format("{0}.{1}", node.Attributes["Graphics"].Value, node.Attributes["Ext"].Value);
			this.m_backgrounds.Add(file);
		}


		/// <summary>
		/// Metoda zobrazí dialogové okno pro načtení mapy
		/// </summary>
		private void LoadMap()
		{
			using(OpenFileDialog open = new OpenFileDialog())
			{
				open.Filter = EditorForm.MAP_EXT;
				if (open.ShowDialog() == DialogResult.OK)
				{
					try
					{
						this.m_map = new World(open.FileName);
						this.m_map.Load();
						this.SetOnStart();
					}
					catch (Exception ex)
					{
						ExceptionView.Show(ex);
						this.m_map = null;
					}

					this.AddLastMap();
					this.Refresh();
					this.SetTitle();
				}
			}
		}

		/// <summary>
		/// Metoda nastaví titulek editoru podle aktuální situace
		/// </summary>
		private void SetTitle()
		{
			if (this.m_map == null)
			{
				this.Text = Resources.Editor;
				return;
			}

			this.Text = string.Format(this.msMainMenu.Enabled? Resources.EditorNormal : Resources.EditorRun, 
				string.IsNullOrEmpty(this.m_map.Name) ? "bezejmenná" : this.m_map.Name);
		}

		/// <summary>
		/// Metoda uloží aktuálně načtenou mapu
		/// </summary>
		/// <param name="saveAs">Pokud je <strong>true</strong>, zobrazí se dialogové okno pro nastavení nového názvu souboru mapy.</param>
		private void SaveMap(bool saveAs)
		{
			if (this.m_map == null)
			{
				return;
			}

			if (this.m_map.Start == null)
			{
				MessageBox.Show("Start nebyl definován.");
				return;
			}

			// Pokud je mapa nová, stejně musíme zobrazit dialogové okno
			if (this.m_map.IsNew)
			{
				saveAs = true;
			}

			if (saveAs)
			{
				using (SaveFileDialog dialog = new SaveFileDialog())
				{
					dialog.Filter = EditorForm.MAP_EXT;
					if (dialog.ShowDialog() == DialogResult.OK && !string.IsNullOrEmpty(dialog.FileName))
					{
						this.m_map.Save(dialog.FileName);
						this.AddLastMap();
					}
				}
			}
			else
			{
				try
				{
					this.m_map.Save();
				}
				catch (Exception ex)
				{
					ExceptionView.Show(ex);
					return;
				}
				this.AddLastMap();
			}

			this.m_change = false;
		}

		/// <summary>
		/// Metoda vykreslí mapu, pokud je načtena, jinak vykreslí text, že není načtena mapa. :))
		/// </summary>
		/// <param name="graphics"><see cref="Graphics"/></param>
		private void DrawWorld(Graphics graphics)
		{
			string text = string.Empty;
			graphics.SmoothingMode = SmoothingMode.HighSpeed;
			graphics.CompositingQuality = CompositingQuality.HighSpeed;
			graphics.InterpolationMode = InterpolationMode.NearestNeighbor;

			// Pokud není načtena žádná mapa
			if(this.m_map == null)
			{
				// Zobrazíme text o tom, že není načtena, ehm. :))
				text = Resources.NoMapLoaded;
				SizeF textSize = graphics.MeasureString(text, this.pnEditor.Font);
				Point position = new Point(
					(this.pnEditor.Width - (int)textSize.Width) / 2, 
					(this.pnEditor.Height - (int)textSize.Height) / 2);
				graphics.DrawString(text, this.pnEditor.Font, Brushes.Black, position);
				return;
			}

			if(this.m_map.BackgroundBitmap != null)
			{
				graphics.DrawImage(this.m_map.BackgroundBitmap, 0,0, this.pnEditor.Width, this.pnEditor.Height);
			}

			int x, y;
			int startx = this.m_camera.X / EditorForm.TILE_SIZE; // Start pozice kamery v ose X
			int starty = this.m_camera.Y / EditorForm.TILE_SIZE; // Start pozice kamery v ose Y
			int width = startx + (this.pnEditor.Width / EditorForm.TILE_SIZE) + EditorForm.TILE_SIZE; // Šířka kamery
			int height = starty + (this.pnEditor.Height / EditorForm.TILE_SIZE) + EditorForm.TILE_SIZE; // Výška kamery

			// Pro všechna pole v rozsahu definovaném startx + width; starty + height
			for (int i = startx; i < width; ++i)
			for (int j = starty; j < height; ++j)
			{
				EditorField field = this.m_map.Get(i, j, false); // Získáme pole mapy
				EditorField fieldF = this.m_map.Get(i, j, true);
				if (field == null && fieldF == null) // Pokud na něm nic není, pokračujeme
				{
					continue;
				}

				// Získáme skutečnou pozici políčka
				x = (i * EditorForm.TILE_SIZE) - this.m_camera.X;
				y = (j * EditorForm.TILE_SIZE) - this.m_camera.Y;
				// Draw both front and back fields
				if (field != null) field.EditorObject.Draw(graphics, x, y);
				if(fieldF != null) fieldF.EditorObject.Draw(graphics, x, y);
			}

			// Vykreslení všech objektů ve hře
			foreach (var field in this.m_map.Objects)
			{
				x = (field.Location.X * EditorForm.TILE_SIZE) - this.m_camera.X;
				y = (field.Location.Y * EditorForm.TILE_SIZE) - this.m_camera.Y;
				if (x > -1 && x <= this.pnEditor.Width && y > -1 && y <= this.pnEditor.Height)
				{
					field.EditorObject.Draw(graphics, x, y);
				}
			}

			// Vykreslení startovní pozice ve hře
			if (this.m_map.Start != null)
			{
				graphics.DrawImage(this.m_player,
					(this.m_map.Start.X * EditorForm.TILE_SIZE) - this.m_camera.X,
					(this.m_map.Start.Y * EditorForm.TILE_SIZE) - this.m_camera.Y,
					this.m_player.Width, this.m_player.Height);
			}

			// Pokud je zvolen mód DELETE
			if (this.m_mode == EditorMode.Delete)
			{
				if (this.m_selectedObjectRectangle == Rectangle.Empty)
				{
					Rectangle rect = new Rectangle(
					this.m_mouse.X * EditorForm.TILE_SIZE, this.m_mouse.Y * EditorForm.TILE_SIZE,
						32*this.m_size,
						32*this.m_size);
					graphics.DrawRectangle(Pens.Red, rect);
				}
			}

			// Pokud je zvolen mód PUT a je vybrán objekt k položení
			if (this.m_selected != null && this.m_mode == EditorMode.Put)
			{
				if (this.m_size == 1)
				{
					this.m_selected.Draw(graphics, this.m_mouse.X * EditorForm.TILE_SIZE, this.m_mouse.Y * EditorForm.TILE_SIZE);
				}
				else
				{
					for (int i = 0; i < this.m_size; ++i)
					for (int j = 0; j < this.m_size; ++j)
					{
						this.m_selected.Draw(graphics, (this.m_mouse.X * EditorForm.TILE_SIZE) + (i * EditorForm.TILE_SIZE),
							(this.m_mouse.Y * EditorForm.TILE_SIZE) + (j * EditorForm.TILE_SIZE));
					}
				}
			}

			// Pokud je vybrán nějaký objekt, vykreslíme kolem něj žluté ohraničení
			if (this.m_selectedObjectRectangle != Rectangle.Empty)
			{
				Rectangle rect = new Rectangle(
					this.m_selectedObjectRectangle.X - this.m_camera.X,
					this.m_selectedObjectRectangle.Y - this.m_camera.Y,
					this.m_selectedObjectRectangle.Width, 
					this.m_selectedObjectRectangle.Height);
				graphics.DrawRectangle(this.m_mode == EditorMode.Delete? Pens.Red : Pens.Yellow, rect);
			}

			// Vykreslení informačního textu
			text = string.Format("Mapa: {0}\nAutor: {1}\nPočet tiles: {2}", this.m_map.Name, this.m_map.Author, this.m_map.Fields.Count);
			graphics.DrawString(text, this.pnEditor.Font, Brushes.Black, new Point(1,1));
			graphics.DrawString(text, this.pnEditor.Font, Brushes.White, Point.Empty);
		}

		private void DisableUI()
		{
			this.msMainMenu.Enabled =
				this.tsToolBarContainer.Enabled =
				this.pnEditor.Enabled = false;
		}

		private void EnableUI()
		{
			this.msMainMenu.Enabled =
				this.tsToolBarContainer.Enabled =
				this.pnEditor.Enabled = true;
		}

		/// <summary>
		/// Metoda uloží aktuálně načtenou mapu, zkopíruje ji do adresáře s mapami<br/>
		/// pod dočasným názvem a spustí ji v enginu hry.
		/// </summary>
		private void DebugMap()
		{
			if (this.m_map == null)
			{
				return;
			}

			string zelviciPath = Path.Combine(Application.StartupPath, "Z2.exe");
			// Kontrola enginu
			if (!File.Exists(zelviciPath))
			{
				MessageBox.Show("Nebyl nalezen Z2.exe, mapu nelze otestovat.", "Chyba", MessageBoxButtons.OK, MessageBoxIcon.Error);
				return;
			}

			this.SaveMap(false); // Uložení mapy

			if (string.IsNullOrEmpty(this.m_map.FileName))
			{
				Debug.WriteLine("Save canceled.");
				return;
			}

			// Zakážeme ovládací prvky
			this.DisableUI();

			this.SetTitle(); // Aktualizace titulku

			ThreadStart threadStart = new ThreadStart(delegate
			{
				string fileNameTemp = Guid.NewGuid().ToString("D"); // Vytvoříme dočasný název
				string path = string.Format(@"{1}\Maps\{0}.zmap",  fileNameTemp, Application.StartupPath); // Cesta k mapám
				
				// Kopírujeme
				FileInfo finfo = new FileInfo(this.m_map.FileName);
				File.Copy(finfo.FullName, path);

				// Spouštíme proces
				ProcessStartInfo pinfo = new ProcessStartInfo(zelviciPath, fileNameTemp);
				pinfo.WorkingDirectory = Application.StartupPath;
				Process process = Process.Start(pinfo);
				process.WaitForExit(); // Čekáme na konec procesu

				File.Delete(path); // Uklidíme po sobě

				// Povolení ovl. prvků a nastavení původního titulku
				this.Invoke(new MethodInvoker(delegate
				{
					this.EnableUI();
					this.SetTitle();
				}));

			});

			new Thread(threadStart).Start(); // Nastartujeme vlákno
		}

		/// <summary>
		/// Metoda se pokusí vybrat objekt na pozici <paramref name="x"/>;<paramref name="y"/>
		/// </summary>
		/// <param name="x">X pozice</param>
		/// <param name="y">Y pozice</param>
		private void TrySelectObject(int x, int y)
		{
			if (this.m_map == null) return;
			List<EditorFieldObject> list = this.m_map.GetObject(x, y);
			if (list!= null && list.Count >0)
			{
				var field = list[0];
				this.m_selectedObjectRectangle = new Rectangle(
					field.Location.X * EditorForm.TILE_SIZE,
					field.Location.Y * EditorForm.TILE_SIZE,
					field.EditorObject.FrameWidth,
					field.EditorObject.FrameHeight);

				this.m_selectedObject = field;
				this.pnEditor.Refresh();
			}
			else
			{
				this.m_selectedObject = null;
				this.m_selectedObjectRectangle = Rectangle.Empty;
			}
		}

		/// <summary>
		/// Metoda spustí akci, obvykle po události <see cref="MouseDown"/> či <see cref="MouseMove"/>.
		/// </summary>
		/// <param name="x">X pozice na mapě</param>
		/// <param name="y">Y pozice na mapě</param>
		private void Action(int x, int y)
		{
			// Korekce pozice
			x = (x + this.m_camera.X) / EditorForm.TILE_SIZE;
			y = (y + this.m_camera.Y) / EditorForm.TILE_SIZE;

			if (this.m_mode == EditorMode.Select && this.m_selectedObject != null && m_mouseDown)
			{
				//this.m_map.Move(, x, y);
				this.m_selectedObject.Location = new Point(x, y);
				this.m_selectedObjectRectangle = new Rectangle(
					x * EditorForm.TILE_SIZE,
					y * EditorForm.TILE_SIZE,
					this.m_selectedObject.EditorObject.FrameWidth,
					this.m_selectedObject.EditorObject.FrameHeight);
				Refresh();
				return;
			}

			// Pokud je mód SELECT START
			if (this.m_mode == EditorMode.SelectStart)
			{
				// Nastavíme startovní pozici
				this.m_map.Start = new Point(x, y);
				this.m_change = true;
				this.Refresh();
				return;
			}

			// Vkládání nových objektů (mód PUT)
			if (this.m_mode == EditorMode.Put && this.m_selected != null)
			{
				EditorField field = this.GetNewField();
				if (field is EditorFieldObject)
				{
					this.m_map.Put(x, y, field, true);
				}
				else
				{
					for (int i = 0; i < this.m_size; ++i)
					for (int j = 0; j < this.m_size; ++j)
					{
						field = this.GetNewField();
						this.m_map.Put(x + i, y + j, field);
					}
				}
				this.m_change = true;
			}

			// Mazání objektů (mód DELETE)
			if (this.m_mode == EditorMode.Delete)
			{
				for(int i = 0; i < this.m_size;++i)
				for (int j = 0; j < this.m_size; ++j)
				{
					this.m_map.Delete(x+i, y+j);
				}
				this.m_change = true;
			}

			this.pnEditor.Refresh();
		}

		#endregion

		#region Override Methods

		protected override void OnKeyDown(KeyEventArgs e)
		{
			this.m_keyPressed = true;
			this.m_key = e.KeyCode;
			base.OnKeyDown(e);
		}

		protected override void OnKeyPress(KeyPressEventArgs e)
		{
			if (this.m_keyPressed)
			{
				if (this.m_key == Keys.Up)
				{
					--this.vScrollBar.Value;
				}
				if (this.m_key == Keys.Down)
				{
					++this.vScrollBar.Value;
				}
				e.Handled = true;
			}
			base.OnKeyPress(e);
		}

		protected override void OnKeyUp(KeyEventArgs e)
		{
			this.m_keyPressed = false;
			base.OnKeyUp(e);
		}

		protected override void OnClosing(CancelEventArgs e)
		{
			if (this.m_change)
			{
				DialogResult result = MessageBox.Show("V této mapě jsou nějaké změny. Opravdu chcete ukončit editor?", "Konec",
					MessageBoxButtons.YesNo, MessageBoxIcon.Question);
				if (result == DialogResult.No)
				{
					e.Cancel = true;
				}
			}
			base.OnClosing(e);
		}

		private void Context(MouseEventArgs e)
		{
			if(this.m_map == null)
			{
				this.pnEditor.ContextMenuStrip = null;
				return;
			}
			int x = (e.X + this.m_camera.X);
			int y = (e.Y + this.m_camera.Y);
			this.TrySelectObject(x, y);

			if (this.m_selectedObject == null)
			{
				this.pnEditor.ContextMenuStrip = this.cmEditorContext;
				this.cmEditorContext.Show(this.pnEditor, e.Location);
				return;
			}

			this.pnEditor.ContextMenuStrip = this.cmObjectContext;
			this.cmObjectContext.Show(this.pnEditor, e.Location);
		}

		#endregion

		#region Event Reactions

		void LastMapItem_Click(object sender, EventArgs e)
		{
			ToolStripMenuItem item = (ToolStripMenuItem)sender;
			string path = ((FileInfo)item.Tag).FullName;
			if (!File.Exists(path))
			{
				return;
			}

			try
			{
				this.m_map = new World(path);
				this.m_map.Load();
				SetOnStart();
			}
			catch (Exception ex)
			{
				ExceptionView.Show(ex);
				this.m_map = null;
			}

			this.SetTitle();
			this.Refresh();
		}

		private void SetOnStart()
		{
			int y = this.m_map.Start.Y - (pnEditor.Height / 64);
			int x = this.m_map.Start.X - (pnEditor.Width / 64);
			if (x < this.hScrollBar.Minimum) x = this.hScrollBar.Minimum;
			if (x > this.hScrollBar.Maximum) x = this.hScrollBar.Maximum;
			if (y < this.vScrollBar.Minimum) x = this.vScrollBar.Minimum;
			if (y > this.vScrollBar.Maximum) x = this.vScrollBar.Maximum;

			this.vScrollBar.Value = y;
			this.hScrollBar.Value = x;
		}

		void Item_Click(object sender, EventArgs e)
		{
			this.m_mode = EditorMode.Put;
			this.m_selected = (EditorObject)((ToolStripMenuItem)sender).Tag;
		}

		void Editor_SizeChanged(object sender, EventArgs e)
		{
			this.pnEditor.Refresh();
		}

		void Editor_Paint(object sender, PaintEventArgs e)
		{
			this.DrawWorld(e.Graphics);
		}

		private void Exit_Trigger(object sender, EventArgs e)
		{
			this.Close();
		}

		private void SaveAsMap_Trigger(object sender, EventArgs e)
		{
			this.SaveMap(true);
		}

		private void SaveMap_Trigger(object sender, EventArgs e)
		{
			this.SaveMap(false);
		}

		private void LoadMap_Trigger(object sender, EventArgs e)
		{
			this.LoadMap();
		}

		private void NewMap_Trigger(object sender, EventArgs e)
		{
			using (MapProperties newMap = new MapProperties())
			{
				if (newMap.ShowDialog() == DialogResult.OK)
				{
					this.m_map = newMap.World;
					this.pnEditor.Refresh();
					this.hScrollBar.Value = this.hScrollBar.Maximum / 2;
					this.vScrollBar.Value = this.vScrollBar.Maximum / 2;
				}
			}
		}

		private void hScrollBar_ValueChanged(object sender, EventArgs e)
		{
			this.m_camera.X = this.hScrollBar.Value * EditorForm.TILE_SIZE;
			this.pnEditor.Refresh();
		}

		private void vScrollBar_ValueChanged(object sender, EventArgs e)
		{
			this.m_camera.Y = this.vScrollBar.Value * EditorForm.TILE_SIZE;
			this.pnEditor.Refresh();
		}

		private void Editor_MouseDown(object sender, MouseEventArgs e)
		{
			if (e.Button == MouseButtons.Right)
			{
				this.Context(e);
				return;
			}

			if (this.m_map == null)
			{
				return;
			}

			Debug.WriteLine(e.X / 32 + ";" + e.Y / 32);
			this.m_mouseDown = true;

			if (this.m_mode == EditorMode.Delete && this.m_selectedObject != null)
			{
				this.m_map.DeleteObject(this.m_selectedObject);
				this.m_selectedObjectRectangle = Rectangle.Empty;
				this.m_selectedObject = null;
				return;
			}

			if (this.m_mode == EditorMode.Select)
			{
				this.m_grabbing = true;
				this.m_grab = new Point(e.X, e.Y);
				int x = (e.X + this.m_camera.X);
				int y = (e.Y + this.m_camera.Y);
				this.TrySelectObject(x, y); // Pokusíme se vybrat objekt na této pozici
				return;
			}

			// Pokud je momentálně mód SELECT
			if (this.m_mode == EditorMode.Select)
			{
				this.TrySelectObject(e.X + this.m_camera.X, e.Y + this.m_camera.Y); // Pokusíme se vybrat objekt na této pozici
				return;
			}


			this.Action(e.X, e.Y);
		}

		private void Editor_MouseUp(object sender, MouseEventArgs e)
		{
			this.m_mouseDown = false;
			this.m_grabbing = false;
		}

		private void Editor_MouseMove(object sender, MouseEventArgs e)
		{
			//if (this.m_grabbing)
			//{
			//    //int dX = (e.X - this.m_grab.X) / EditorForm.TILE_SIZE;
			//    //int dY = (e.Y - this.m_grab.Y) / EditorForm.TILE_SIZE;
			//    //this.hScrollBar.Value = dX < this.hScrollBar.Minimum ? this.hScrollBar.Minimum : dX;
			//    //this.vScrollBar.Value = dY < this.vScrollBar.Minimum ? this.hScrollBar.Minimum : dY;
			//    return;
			//}

			if (this.m_mode == EditorMode.Delete)
			{
				int x = (e.X + this.m_camera.X);
				int y = (e.Y + this.m_camera.Y);
				this.TrySelectObject(x, y);
			}

			if (this.m_mode == EditorMode.Select && this.m_mouseDown)
			{
				this.Action(e.X, e.Y);
				return;
			}

			if (this.m_mode == EditorMode.Put || this.m_mode == EditorMode.Delete)
			{
				this.m_mouse = new Point(e.X / EditorForm.TILE_SIZE, e.Y / EditorForm.TILE_SIZE);
				this.Refresh();
			}

			if (this.m_mouseDown && this.m_mode != EditorMode.Select)
			{
				if (this.m_map == null)
				{
					return;
				}

				this.Action(e.X, e.Y);
			}
		}

		private void miDelete_Click(object sender, EventArgs e)
		{
			this.m_mode = EditorMode.Delete;
			MapTriggerGroup.Select.IsChecked = false;
			this.pnEditor.Refresh();
		}

		private void miStart_Click(object sender, EventArgs e)
		{
			this.m_mode = EditorMode.SelectStart;
			this.pnEditor.Refresh();
		}

		private void miSelect_Click(object sender, EventArgs e)
		{
			this.m_mode = EditorMode.Select;
			MapTriggerGroup.Delete.IsChecked = false;
			this.pnEditor.Refresh();
		}

		private void TestMap_Trigger(object sender, EventArgs e)
		{
			this.DebugMap();
		}

		private void MapProperties_Trigger(object sender, EventArgs e)
		{
			if (this.m_map == null)
			{
				return;
			}

			using (MapProperties properties = new MapProperties(this.m_map))
			{
				if (properties.ShowDialog() == DialogResult.OK)
				{
					this.pnEditor.Refresh();
				}
			}
		}

		private void tbSize_TextChanged(object sender, EventArgs e)
		{
			int size;
			ToolStripComboBox combo = (ToolStripComboBox)sender;
			if (int.TryParse(combo.Text, out size))
			{
				this.m_size = size < 1 ? 1 : (size > 20 ? 20 : size);
			}
		}

		private void Context_Closed(object sender, ToolStripDropDownClosedEventArgs e)
		{
			this.pnEditor.ContextMenuStrip = null;
		}

		private void miObjectProperties_Click(object sender, EventArgs e)
		{
			if (this.m_selectedObject == null)
			{
				return;
			}
			using (ObjectProperties properties = new ObjectProperties(this.m_selectedObject))
			{
				if (properties.ShowDialog() == DialogResult.OK)
				{
					this.Refresh();
				}
			}

		}

		private void miObjectDelete_Click(object sender, EventArgs e)
		{
			if (this.m_selectedObject == null || this.m_map == null)
			{
				return;
			}

			this.m_map.DeleteObject(this.m_selectedObject);
			this.m_selectedObjectRectangle = Rectangle.Empty;
			this.m_selectedObject = null;
			this.pnEditor.Refresh();
		}

		private void Log_Triggered(object sender, EventArgs e)
		{
			using (LogView view = new LogView())
			{
				view.ShowDialog();
			}
		}

		private void About_Trigger(object sender, EventArgs e)
		{
			StringBuilder builder = new StringBuilder();
			builder.AppendLine("Želvíci Editor © 2010 David Schwarz");
			builder.AppendLine("Icons © ASPNETicons");
			builder.Append("Icons © FAM FAM FAM");
			MessageBox.Show(builder.ToString(), "O programu", MessageBoxButtons.OK, MessageBoxIcon.Information);
		}
		
		private void Script_Triggered(object sender, EventArgs e)
		{
			if(this.m_map == null) return;
			using (ScriptEditor editor = new ScriptEditor(this.m_map))
			{
				editor.ShowDialog();
			}
		}

		private void Autosave_Tick(object sender, EventArgs e)
		{
			if (this.m_map == null) return;
			if (DateTime.Now.Subtract(this.m_lastAutoSave) < this.m_autosaveTime) return;

			this.m_lastAutoSave = DateTime.Now;
			Debug.WriteLine("Autosaving..");

			string fileName = string.IsNullOrEmpty(this.m_map.FileName) ?
				Path.Combine(Path.GetTempPath(), Convert.ToString(this.m_map.GetHashCode())) : this.m_map.FileName;


			Debug.WriteLine(string.Format("Saving to {0}", fileName));

			this.Invoke(new MethodInvoker(delegate { this.DisableUI(); }));
			try
			{
				this.m_map.Save(fileName);
			}
			catch (Exception ex)
			{
				Debug.WriteLine("Save failed: " + ex.Message); ;
			}
			finally
			{
				this.Invoke(new MethodInvoker(delegate { this.EnableUI(); }));
			}
		}

		private void AutoSave_Triggered(object sender, EventArgs e)
		{
			if (this.m_autosave == null) return;
			this.m_autosave.Enabled = SettingsGroup.AutoSave.IsChecked;
			if (SettingsGroup.AutoSave.IsChecked)
				this.m_autosave.Start();
			else
				this.m_autosave.Stop();
			Debug.WriteLine("Autosave is " + this.m_autosave.Enabled);
		}

		private void Background_Triggered(object sender, EventArgs e)
		{
			if (this.m_map == null) return;
			using (BackgroundEditor editor = new BackgroundEditor(this.m_map, this.m_backgrounds))
			{
				editor.ShowDialog();
			}
		}

		#endregion

	}
}
